/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Header protection
#pragma once
#ifndef SKUGO_PLUGIN_HPP
#define SKUGO_PLUGIN_HPP

// Includes
#include "SkugoTypes.hpp"
#include "SkugoPacket.hpp"
#include "SkugoBuffer.hpp"
#include <map>
#include <string>

namespace Skugo
{
  // Using directives
  using std::map;
  using std::string;

  // Forward declaration
  class IWriter;
  class Client;
  class PluginContainer;

  // The plugin provides functionality for creating a remote instance,
  // as well as sending, and receiving data to and from that instance
  class Plugin
  {
  public:

    // Constructor
    Plugin(const string& remoteTypeName, Client* client);

    // Destructor
    ~Plugin();

    // Internally called when packet data is received from our external counterpart(s).
    void Receive(const char* data, sizeType size, PacketType::Enum type);

  protected:

    // Called when packet data is received from our external counterpart(s).
    virtual void OnReceive(const char* data, sizeType size) = 0;

    // Called when an item gets dragged and dropped onto our external counterpart.
    virtual void OnDrop(const char* data, sizeType size, Plugin* source, void* sourceData);

    // Send data to our external counterpart(s).
    void Send(const char* data, sizeType size);
    void Send(const Buffer& buffer);
    void Send(const char* data, sizeType size, PacketType::Enum type);
    void Send(const Buffer& buffer, PacketType::Enum type);

    // Create the remote plugin.
    void CreateRemotePlugin();

    // Two parts to create a creation packet (so we can fill our own data in it)
    Buffer BeginCreateRemotePlugin();
    void EndCreateRemotePlugin(Buffer& outgoing);

  private:

    // Get rid of assignment and copy
    Plugin& operator =(const Plugin&);
    Plugin(const Plugin&);

    // Destroy the remote plugin (helper).
    void DestroyRemote();

  public:

    // Store the guid for this object
    const Guid InstanceGuid;

  private:

    // Store the remote type name
    string RemoteTypeName;

    // Maintains the connection with the external plugin
    IWriter* Writer;
    
    // The plugin container contains the list of all plugins (including us)
    PluginContainer* Plugins;

    // A guid counter that lets us assign a unique id to a new instance of a plugin.
    static Guid GuidCounter;
  };

  // A plugin container contains a list of plugins.
  class PluginContainer
  {
  public:
    
    // Add a plugin to the container.
    void AddPlugin(Plugin* plugin);
    
    // Remove a plugin to from container.
    void RemovePlugin(Plugin* plugin);
    
    // Get a plugin by guid.
    Plugin* GetPlugin(Guid pluginGuid);
    
    // Distroys all associated plugins.
    void DestroyAllPlugins();

  private:

    // A map from guid to plugins
    map<Guid, Plugin*> Plugins;
  };
}

// End header protection
#endif
